<!doctype html>
<html lang="zh" class="no-js">
  <head>
    <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="generator" content="Hugo 0.75.1" />


<META NAME="ROBOTS" CONTENT="INDEX, FOLLOW">
<meta name="google-site-verification" content="9Ph9Wm3pZwsQkEkjY7ZubNjjiPp6nODqkLZDjJgoaCY" />



<link rel="shortcut icon" href="/favicons/favicon.ico?" >
<link rel="apple-touch-icon" href="/favicons/apple-touch-icon-180x180.png" sizes="180x180">
<link rel="icon" type="image/png" href="/favicons/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/favicons/android-36x36.png" sizes="36x36">
<link rel="icon" type="image/png" href="/favicons/android-48x48.png" sizes="48x48">
<link rel="icon" type="image/png" href="/favicons/android-72x72.png" sizes="72x72">
<link rel="icon" type="image/png" href="/favicons/android-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/favicons/android-144x144.png" sizes="144x144">
<link rel="icon" type="image/png" href="/favicons/android-192x192.png" sizes="192x192">

<title>从HelloWold开始，深入浅出C&#43;&#43; 20 Coroutine TS | 努力探求技术边界</title><meta property="og:title" content="从HelloWold开始，深入浅出C&#43;&#43; 20 Coroutine TS" />
<meta property="og:description" content="分析C&#43;&#43;20协程的使用和原理分析" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://gqw.github.io/blog/2020/10/01/%E4%BB%8Ehellowold%E5%BC%80%E5%A7%8B%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BAc-20-coroutine-ts/" />
<meta property="article:published_time" content="2020-10-01T00:00:00+00:00" />
<meta property="article:modified_time" content="2020-11-19T09:55:41+08:00" /><meta property="og:site_name" content="努力探求技术边界" />
<meta itemprop="name" content="从HelloWold开始，深入浅出C&#43;&#43; 20 Coroutine TS">
<meta itemprop="description" content="分析C&#43;&#43;20协程的使用和原理分析">
<meta itemprop="datePublished" content="2020-10-01T00:00:00+00:00" />
<meta itemprop="dateModified" content="2020-11-19T09:55:41+08:00" />
<meta itemprop="wordCount" content="1550">



<meta itemprop="keywords" content="" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="从HelloWold开始，深入浅出C&#43;&#43; 20 Coroutine TS"/>
<meta name="twitter:description" content="分析C&#43;&#43;20协程的使用和原理分析"/>





<link rel="preload" href="/scss/main.min.276cf758ea51ad17445a946225f8c9b77391699167c3fb92541c8dfce581a44c.css" as="style">
<link href="/scss/main.min.276cf758ea51ad17445a946225f8c9b77391699167c3fb92541c8dfce581a44c.css" rel="stylesheet" integrity="">


<script
  src="https://code.jquery.com/jquery-3.5.1.min.js"
  integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
  crossorigin="anonymous"></script>



  </head>
  <body class="td-page td-blog">
    <header>
      
<nav class="js-navbar-scroll navbar navbar-expand navbar-dark flex-column flex-md-row td-navbar">
        <a class="navbar-brand" href="/">
		<span class="navbar-logo"><svg height="232pt" viewBox="0 0 232 232" width="232pt" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.1 0 0 -.1 0 232)"><path d="m1385 2213c-22-20-68-49-103-65-73-35-137-40-282-20-189 26-321-20-336-118-5-33-9-36-40-36-64 0-130-93-188-266-29-86-48-221-41-298 9-114 18-162 30-166 7-3 30-1 52 5l39 11 22 96c21 90 68 198 111 253l18 23 6-47c17-118 89-171 231-169 65 1 119 11 276 54 161 45 209 54 275 54 144 0 205-61 205-204v-57l61-12c34-7 62-11 64-9 10 10 25 144 25 223 0 173-46 299-144 4e2l-55 56-54-31c-62-35-89-40-37-7 75 47 141 138 156 215l7 32-58-35c-66-39-139-58-207-53l-47 3 20 45c19 42 44 148 37 155-2 2-21-13-43-32z"/><path d="m516 1230c-49-10-95-21-102-24-16-6-19-76-4-76 17 0 50-63 50-94 0-68 33-149 79-197 56-57 103-73 222-73 73 0 93 4 142 28 97 47 167 157 167 261 0 44 1 45 33 45s32 0 33-53c1-75 36-148 99-206 65-58 120-76 231-75 170 2 255 80 279 254 8 63 27 105 50 114 11 4 15 16 13 38-3 32-5 33-78 50-170 41-351 33-479-20-89-37-211-38-285-4-111 52-292 65-450 32zm385-59c78-30 114-70 113-126-1-62-47-150-97-186-53-39-108-52-188-47-131 10-198 78-207 212-6 91 12 129 71 157 58 27 226 22 308-10zm708 11c73-36 97-114 67-215-22-76-64-123-129-142-1e2-30-201-14-266 41-75 64-114 187-76 241 40 57 145 91 280 92 67 1 99-4 124-17z"/><path d="m783 1105c-29-20-46-75-34-107 11-30 57-68 81-68 35 0 78 22 89 46 14 32 14 85-2 105-30 39-96 51-134 24z"/><path d="m1473 1105c-29-20-46-75-34-106 5-13 19-35 31-48 19-19 30-22 64-18 54 7 86 44 86 97 0 70-89 116-147 75z"/><path d="m459 537c-25-7-59-23-76-35-89-63-92-230-6-301 60-49 134-59 231-29l32 10v74c0 59 3 74 15 74 8 0 15 5 15 10 0 6-33 10-80 10s-80-4-80-10c0-5 11-10 25-10 25 0 25-1 25-80v-80h-35c-77 0-125 71-125 182 1 116 53 183 138 176 34-3 37-6 51-50 10-34 20-48 33-48 15 0 18 8 18 45 0 38-4 47-23 54-45 17-112 20-158 8z"/><path d="m960 538c-77-21-124-92-124-188 0-84 31-145 91-178 29-16 45-33 53-56 15-48 84-73 150-57l35 9-41 1c-44 1-70 21-76 58-2 18 6 27 42 45 71 36 105 116 94 215-8 71-63 135-128 152-51 13-49 13-96-1zm89-18c32-16 51-83 51-173-1-134-39-192-116-173-77 20-90 288-17 344 20 14 57 16 82 2z"/><path d="m1367 525c18-13 24-26 38-85 2-8 22-72 43-142 22-70 37-130 34-133s6-5 21-5c24 0 28 7 52 88 48 162 62 198 69 181 4-9 24-72 44-140 31-103 41-125 58-127 18-3 24 10 53 110 74 260 70 248 96 248 14 0 25 5 25 10 0 6-27 10-60 10s-60-4-60-10c0-5 12-10 26-10 22 0 24-3 18-22-4-13-20-72-35-131-15-60-29-111-32-113-3-4-48 141-62 203-2 10-7 28-11 41-5 19-3 22 20 22 14 0 26 5 26 10 0 6-33 10-80 10-44 0-80-4-80-9s11-11 23-13l24-3-29-1e2c-16-55-31-111-34-124-11-52-18-41-46 65-42 159-42 164-18 164 11 0 20 5 20 10 0 6-34 10-82 10-76 0-81-1-61-15z"/></g></svg></span><span class="text-uppercase font-weight-bold">努力探求技术边界</span>
	</a>
	<div class="td-navbar-nav-scroll ml-md-auto" id="main_navbar">
		<ul class="navbar-nav mt-2 mt-lg-0">
			
			
			<li class="nav-item mr-4 mb-2 mb-lg-0">
				
				
				
				
				
				
				<a class="nav-link active" href="/blog/" ><span class="active">博客</span></a>
			</li>
			
			<li class="nav-item mr-4 mb-2 mb-lg-0">
				
				
				
				
				
				
				<a class="nav-link" href="/about/" ><span>关于</span></a>
			</li>
			
			
			
		</ul>
	</div>
	<div class="navbar-nav d-none d-lg-block">
<input type="search" class="form-control td-search-input" placeholder="&#xf002 站内搜索…" aria-label="站内搜索…" autocomplete="off">

</div>
</nav>

    </header>
    <div class="container-fluid td-outer">
      <div class="td-main">
        <div class="row flex-xl-nowrap">
          <div class="col-12 col-md-3 col-xl-2 td-sidebar d-print-none">
            




<div id="td-sidebar-menu" class="td-sidebar__inner">
  
  <form class="td-sidebar__search d-flex align-items-center">
    
<input type="search" class="form-control td-search-input" placeholder="&#xf002 站内搜索…" aria-label="站内搜索…" autocomplete="off">


    <button class="btn btn-link td-sidebar__toggle d-md-none p-0 ml-3 fas fa-bars" type="button" data-toggle="collapse" data-target="#td-section-nav" aria-controls="td-docs-nav" aria-expanded="false" aria-label="Toggle section navigation">
    </button>
  </form>
  
  <nav class="collapse td-sidebar-nav" id="td-section-nav">
    
    






<ul class="td-sidebar-nav__section pr-md-3">
  <li class="td-sidebar-nav__section-title">
    <a  href="/blog/" class="align-left pl-0 pr-2 active td-sidebar-link td-sidebar-link__section">博客</a>
  </li>
  <ul>
    <li class="collapse show" id="blog">
      
      
      
      
      
      
      <a class="td-sidebar-link td-sidebar-link__page " id="m-blog20201229tcprt" href="/blog/2020/12/29/tcprt/">tcprt</a>
      
      
      
      
      
      <a class="td-sidebar-link td-sidebar-link__page " id="m-blog20201118c-e5ba8fe58897e58c96e5928ce58f8de5ba8fe58897e58c96e5b7a5e585b7" href="/blog/2020/11/18/c-%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%B7%A5%E5%85%B7/">C&#43;&#43;序列化和反序列化工具</a>
      
      
      
      
      
      <a class="td-sidebar-link td-sidebar-link__page  active" id="m-blog20201001e4bb8ehellowolde5bc80e5a78be6b7b1e585a5e6b585e587bac-20-coroutine-ts" href="/blog/2020/10/01/%E4%BB%8Ehellowold%E5%BC%80%E5%A7%8B%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BAc-20-coroutine-ts/">C&#43;&#43; 20 Coroutine</a>
      
      
    </li>
  </ul>
</ul>

  </nav>
</div>




          </div>
          <div class="d-none d-xl-block col-xl-2 td-toc d-print-none">
            






<div class="td-page-meta ml-2 pb-1 pt-2 mb-0" style="display: none">





<a href="https://github.com/gqw/edit/master/content/zh/blog/coroutine/coroutine.md" target="_blank"><i class="fa fa-edit fa-fw"></i> 编辑此页</a>
<a href="https://github.com/gqw/issues/new?title=%e4%bb%8eHelloWold%e5%bc%80%e5%a7%8b%ef%bc%8c%e6%b7%b1%e5%85%a5%e6%b5%85%e5%87%baC&#43;&#43;%2020%20Coroutine%20TS" target="_blank"><i class="fab fa-github fa-fw"></i> 提交文档问题</a>


<a href="https://github.com/gqw/issues/new" target="_blank"><i class="fas fa-tasks fa-fw"></i> 提交项目问题</a>

</div>






<nav id="TableOfContents">
  <ul>
    <li><a href="#缘起">缘起</a></li>
    <li><a href="#从helloworld说起">从<code>HelloWorld</code>说起</a></li>
    <li><a href="#多线程callback解决方案">多线程+<code>Callback</code>解决方案</a></li>
    <li><a href="#主角登场">主角登场</a></li>
    <li><a href="#先从定义说起">先从定义说起</a></li>
    <li><a href="#原理剖析">原理剖析</a>
      <ul>
        <li><a href="#协程函数展开">协程函数展开</a></li>
        <li><a href="#co_await-表达式">co_await 表达式</a></li>
        <li><a href="#承诺promise对象">承诺（promise）对象</a></li>
        <li><a href="#协程句柄-coroutine-handle">协程句柄 (coroutine handle)</a></li>
        <li><a href="#协程状态-coroutine-state">协程状态 (coroutine state)</a></li>
        <li><a href="#协程跳转">协程跳转</a></li>
      </ul>
    </li>
    <li><a href="#总结">总结</a></li>
    <li><a href="#参考引用">参考引用</a></li>
  </ul>
</nav>



          </div>
          <main class="col-12 col-md-9 col-xl-8 pl-md-5 pr-md-4" role="main">
            <a class="btn btn-lg td-rss-button d-none d-lg-block" href="https://gqw.github.io/blog/index.xml" target="_blank" >
             <img src="http://www.runoob.com/images/rss.gif" width="36" height="14">
            </a>
            
<div class="td-content">
	<h1>从HelloWold开始，深入浅出C&#43;&#43; 20 Coroutine TS</h1>
	<div class="lead">分析C++20协程的使用和原理分析</div>
	<div class="td-byline mb-4">
		By <b>顾起威 (<a href="https://gqw.github.io">@gqw</a>)</b> |
		<time datetime="2020-10-01" class="text-muted">2020-10-01</time>
	</div>
	
	<h2 id="缘起">缘起</h2>
<p>前一阵子在查看<code>asio</code>库的时候看到在example目录中已经提供了<code>coroutines_ts</code>代码。很是好奇，便慢慢翻看代码，在感叹Coroutine给异步编程带来的优雅简洁的同时也带来了许多概念上的复杂和难以理解。由于C++ Coroutine还是一个比较新的概念，各个编译器厂商的实现还处于实验性的阶段<sup><a href="#ref_1">[1]</a></sup> ，网上的资料少而松散。由此内心涌出一个念头想把自己的学习过程记录下来以期帮助后面学习的人。</p>
<h2 id="从helloworld说起">从<code>HelloWorld</code>说起</h2>
<p>再过两年这个世界的第一条<code>HelloWorld</code>代码就要有50年历史了<sup><a href="#ref_2">[2]</a></sup> ，在此先提前蹭下热点，:)&hellip;</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;iostream&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> 
  <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">cout</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#4e9a06">&#34;hello, world&#34;</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">endl</span><span style="color:#000;font-weight:bold">;</span> 
  <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><center><div style="color:orange; display: inline-block; color: #999; padding: 2px;">代码1</div></center></br>
<p>这是一段平淡无奇的符合<code>C++98</code>标准的代码，你甚至可以用古董级编译器<code>GCC 4.3</code>都能编译通过<sup><a href="#ref_3">[3]</a></sup>。但是我希望通过对这段代码的演化带领大家学习了解C++ 20中的Coroutine。</p>
<p>这段代码的作用很简单，打印一条&quot;hello, world&quot;，执行的也很快。但是这个世界并不总是那么简单美好，如果打印的逻辑非常耗时（特别是读取磁盘文件和进行网络请求时）而又需要频繁的调用就有些尴尬了，好了，我们改下代码来模拟这种情形：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;iostream&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;thread&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;chrono&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">using</span> <span style="color:#204a87;font-weight:bold">namespace</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">chrono_literals</span><span style="color:#000;font-weight:bold">;</span>

<span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">remote_query</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#8f5902;font-style:italic">// 假设这是个远程网络请求
</span><span style="color:#8f5902;font-style:italic"></span>  <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">this_thread</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">sleep_for</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000">s</span><span style="color:#000;font-weight:bold">);</span>
  <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">cout</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#4e9a06">&#34;hello, world&#34;</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">endl</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>

<span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> 
  <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(;;)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">remote_query</span><span style="color:#000;font-weight:bold">();</span>
  <span style="color:#000;font-weight:bold">}</span>
  <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><center><div style="color:orange; display: inline-block; color: #999; padding: 2px;">代码2</div></center></br>
<p>改过后的代码不停的进行耗时的远程调用，每次远程调用需要消耗4秒的时间才能返回。可以看到，每次进行调用时程序都要暂停住（也就是卡住）而做不了其它事情。如果这是个带<code>UI</code>(用户界面)的程序，每次进行调用界面就要卡住，用户肯定是不能忍受的。</p>
<hr>
<p><strong>注意</strong></p>
<p>上面的代码由于使用了<code>thread</code><sup><a href="#ref_4">[4]</a></sup>(C++ 11)和<code>chrono_literals</code><sup><a href="#ref_5">[5]</a></sup>(C++ 14)，所以编译器需要支持C++ 14标准。</p>
<hr>
<h2 id="多线程callback解决方案">多线程+<code>Callback</code>解决方案</h2>
<p>为了解决上面的问题，我们对上面的代码进行如下改造：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;iostream&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;thread&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;chrono&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">using</span> <span style="color:#204a87;font-weight:bold">namespace</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">chrono_literals</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;string&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;future&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>
<span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">remote_query</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">uint32_t</span> <span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">function</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">void</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">uint32_t</span><span style="color:#000;font-weight:bold">)</span><span style="color:#ce5c00;font-weight:bold">&gt;&amp;&amp;</span> <span style="color:#000">callback</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> 
 <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#204a87;font-weight:bold">thread</span> <span style="color:#000">t</span><span style="color:#000;font-weight:bold">(</span>
      <span style="color:#000;font-weight:bold">[</span><span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">callback</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">move</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">callback</span><span style="color:#000;font-weight:bold">)]()</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">this_thread</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">sleep_for</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000">s</span><span style="color:#000;font-weight:bold">);</span>
        <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">cout</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">to_string</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#4e9a06">&#34; times query: Hello, world!&#34;</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">endl</span><span style="color:#000;font-weight:bold">;</span> 
        <span style="color:#000">callback</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">);</span>
      <span style="color:#000;font-weight:bold">});</span>
  <span style="color:#000">t</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">detach</span><span style="color:#000;font-weight:bold">();</span>
<span style="color:#000;font-weight:bold">}</span>

<span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">remote_callback</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">uint32_t</span> <span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
 <span style="color:#000">remote_query</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">++</span><span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">remote_callback</span><span style="color:#000;font-weight:bold">);</span>
<span style="color:#000;font-weight:bold">}</span>

<span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#000">remote_query</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">remote_callback</span><span style="color:#000;font-weight:bold">);</span>
  <span style="color:#204a87;font-weight:bold">while</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">true</span><span style="color:#000;font-weight:bold">)</span>
  <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">this_thread</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">sleep_for</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000">s</span><span style="color:#000;font-weight:bold">);</span>
    <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">cout</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#4e9a06">&#34;main thread doing other things...&#34;</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">endl</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">}</span>
  <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><center><div style="color:orange; display: inline-block; color: #999; padding: 2px;">代码3</div></center></br>
<p>打印结果：</p>
<pre><code>main thread doing other things...
main thread doing other things...
main thread doing other things...
main thread doing other things...
1 times query: Hello, world!
main thread doing other things...
main thread doing other things...
main thread doing other things...
main thread doing other things...
2 times query: Hello, world!
main thread doing other things...
main thread doing other things...
</code></pre><p>是的，我们可以通过多线程解决卡顿的问题，但这是个好的方案吗？不说线程的开销（其实创建和销毁一个线程无论从时间还是空间上来说都是非常大的开销），单从代码的组织来看也足够让人头疼的了。上面的代码只是循环调用同样的远程方法，如果<code>remote_callback</code>调用<code>remote_query1</code>(另一个远程调用)，<code>remote_query1</code>回调中再调用<code>remote_query2</code>&hellip;如此下去代码逻辑将会非常恐怖。如果不能明白我说的是什么意思，<code>callback hell</code> 了解下:)， 为什么说是恐怖，因为这完全不符合人类的思维逻辑。人类喜欢的代码是有条不紊的按步执行，而不是在各个回调中跳来跳去。最好就像前面的同步代码，一个请求走完再执行下一个请求。</p>
<h2 id="主角登场">主角登场</h2>
<p>有没有什么方法解决这种复杂性呢？有的，技术牛人们早已发现这个问题并且给出了设计模型，有类似于<code>libevent</code>的<code>Reactor</code>模式<sup><a href="#ref_6">[6]</a></sup>，也有<code>asio</code>的<code>Proactor</code>模式<sup><a href="#ref_7">[7]</a></sup>。但<code>Reactor</code>，<code>Proactor</code>模式的核心思想都是通过事先注册回调函数，收到消息再根据消息内容查找并执行回调，有效的将层层包裹的回调给摊平以达到降低回调函数管理的复杂度。但是无论<code>Reactor</code>还是<code>Proactor</code>调用逻辑和回调逻辑还是被硬生生的给割裂开了。如何弥合这里的割裂呢？这就需要请出我们今天的主角<code>Corouter</code>了，还是先看代码吧：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;iostream&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;thread&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;chrono&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">using</span> <span style="color:#204a87;font-weight:bold">namespace</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">chrono_literals</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;string&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;future&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>
<span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">future</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">string</span><span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">remote_query</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">uint32_t</span> <span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">async</span><span style="color:#000;font-weight:bold">([</span><span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">]()</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">this_thread</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">sleep_for</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000">s</span><span style="color:#000;font-weight:bold">);</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">to_string</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#4e9a06">&#34; times query: Hello, world!&#34;</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">});</span>
<span style="color:#000;font-weight:bold">}</span>

<span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">future</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">void</span><span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">remote_query_all</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> 
  <span style="color:#204a87;font-weight:bold">uint32_t</span> <span style="color:#000">query_index</span>  <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(;;)</span>
  <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">string</span> <span style="color:#000">ret</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">co_await</span> <span style="color:#000">remote_query</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">++</span><span style="color:#000">query_index</span><span style="color:#000;font-weight:bold">);</span>
    <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">cout</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">ret</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">endl</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">}</span>
<span style="color:#000;font-weight:bold">}</span>

<span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#000">remote_query_all</span><span style="color:#000;font-weight:bold">();</span>

  <span style="color:#204a87;font-weight:bold">while</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">true</span><span style="color:#000;font-weight:bold">)</span>
  <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">this_thread</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">sleep_for</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000">s</span><span style="color:#000;font-weight:bold">);</span>
    <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">cout</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#4e9a06">&#34;main thread doing other things...&#34;</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">endl</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">}</span>
  <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><center><div style="color:orange; display: inline-block; color: #999; padding: 2px;">代码4</div></center></br>
<p>打印结果：</p>
<pre><code>main thread doing other things...
main thread doing other things...
main thread doing other things...
1 times query: Hello, world!
main thread doing other things...
main thread doing other things...
main thread doing other things...
main thread doing other things...
2 times query: Hello, world!
main thread doing other things...
main thread doing other things...
main thread doing other things...
</code></pre><p>看到了吗？打印结果还是一样的，但是回调没有了,  从代码上看函数<code>remote_query_all</code>的逻辑也是在一个for循环中按步执行。好了，讲到这里只是希望你能直观的体会到使用Coroutine的作用和好处，但是你心中一定会有n个问号。</p>
<ul>
<li>这不科学呀，<code>remote_query</code>返回值不是<code>std::future&lt;std::string&gt;</code>吗？ 怎么直接赋值给<code>std::string</code>了？</li>
<li><code>co_await</code> 又是什么鬼？</li>
<li><code>remote_query</code> 是个异步调用,  返回的<code>ret</code>直接用会不会崩溃？</li>
<li>&hellip;</li>
</ul>
<p>请少安毋躁， 暂时按下心中的疑惑，因为这边的水很深，坑很大，不是三言两语能够解释的清楚的。我会尽量努力在后面的内容中消除你心中的困惑。</p>
<h2 id="先从定义说起">先从定义说起</h2>
<p>翻开<code>cppreference</code>看看协程的定义<sup><a href="#ref_8">[8]</a></sup>：</p>
<pre><code>协程是能暂停执行以在之后恢复的函数。协程是无栈的：它们通过返回到调用方暂停执行，并且从栈分离存储恢复所要求的数据。这允许编写异步执行的顺序代码（例如不使用显式的回调来处理非阻塞 I/O），还支持对惰性计算的无限序列上的算法及其他用途。

若函数的定义做下列任何内容之一，则它是协程：

. 用 co_await 运算符暂停执行，直至恢复
. 用关键词 co_yield 暂停执行并返回一个值
. 用关键词 co_return 完成执行并返回一个值
</code></pre><p>读完之后似乎还是什么都不知道，好吧，现在我们试着逐句翻译成“白话文”。</p>
<ul>
<li>
<p>协程是能暂停执行以在之后恢复的函数。</p>
<p>这里的协程就是前面说的<code>Coroutine</code>, 这句说出了协程的本质。首先它是一个函数，然后这个函数与普通函数的区别是能够“暂停”和“恢复”执行代码。所以协程最关键的点是“暂停”和“恢复”。用一段代码说明下：</p>
<pre><code>  void foo() {
      code1;
      code2;
      code3;
  }
    
 int main() {
      foo();
      return 0;
  }
</code></pre><center><div style="color:orange; display: inline-block; color: #999; padding: 2px;">代码5</div></center></br>
<p>这是一段普通的代码，代码从main函数开始，先调用<code>foo</code>函数，接着依次调用<code>code1</code>、<code>code2</code>、<code>code3</code>，然后返回<code>main</code>函数，最后执行<code>return 0</code>。这种执行顺序是我们见到的最常见的顺序，代码逐行执行，按部就班符合我们的预期。但是如果是协程，代码就可以在执行完<code>code1</code>后立马返回到main函数，在适当时候再返回<code>foo</code>执行<code>code1</code>后面的代码<code>code2</code>——这就是前面所谓的“暂停”和“恢复”。其实仔细想想所谓协程也只是计算机执行顺序的一种规范，有点类似goto指令，都是用于改变常规的代码执行顺序，只是这些规范我们用的比较少见而已。其实翻阅维基百科的“协程”页面<sup><a href="#ref_9">[9]</a></sup>，你会发现其实协程历史并不短，起码已经超过半个世纪了。但是直到最近几年才开始火热是有原因的，就像goto语句一样，过多的自由有时也是一种灾难（代码逻辑混乱，难以维护）。但是随着计算机硬件的发展（多核多线程）和一些成熟的案例（如腾讯的<code>libco</code><sup><a href="#ref_10">[10]</a></sup>和<code>golang</code><sup><a href="#ref_11">[11]</a></sup>）出现，让人们认识到了协程的优势和闪光点。</p>
</li>
<li>
<p>协程是无栈的。</p>
<p>“无栈” 两个字隐藏了太多的概念，对于一个完全没有接触过协程的人来说这个概念太含糊和陌生了。这里的“无栈”是相对于协程的另一种实现方式“有栈”协程而言的。在介绍这两个概念前先看下普通函数调用堆栈。函数堆栈非正式的理解就是函数调用时申请的用于存放参数和临时变量的内存块，按照后进先出（LIFO, Last In First Out）的规则分配内存，因为只有push和pop两种操作所以比较简单和快捷，由编译器自动管理其分配内存的生命周期。 在https://godbolt.org/里写下测试代码得到如下汇编指令：</p>
<center>
  <img style="border-radius: 0.3125em;
  box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
  src="https://i.loli.net/2020/10/02/g2thx7oSOYybrpE.png">
  <br>
  <div style="color:orange; border-bottom: 0px solid #d9d9d9;
  display: inline-block;
  color: #999;
  padding: 2px;">图1 函数调用汇编分析源码</div>
</center>
<p>经过简单整理可得到堆栈的使用情况如下图所示：</p>
<center>
  <img style="border-radius: 0.3125em;
  box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
  src="https://i.loli.net/2020/10/02/zdcHC9wYpSAfrv3.png">
  <br>
  <div style="color:orange; border-bottom: 0px solid #d9d9d9;
  display: inline-block;
  color: #999;
  padding: 2px;">图2 函数调用堆栈变化</div>
</center>
<hr>
<p><strong>注意</strong></p>
<p>上面的代码是使用<code>x64</code>位的<code>msvc</code>编译的，和经常见到<code>x86</code>版本相比64位版本的更加简洁易懂，少了“基址指针寄存器”的概念所以省去了保存和恢复基址指针的操作。这里也没有常规的push和pop操作，而是直接通过sub和add相应的地址偏移获得相同的效果。</p>
<hr>
<p>通过上面的分析我们可以看到，每次进行函数调用我们都需要为被调用的函数分配栈空间，用以保存调用参数和局部变量。为了实现协程，我们必须要实现前面说的“暂停”和“恢复”特性。有栈协程为了实现这两个特性做法是想办法在暂停后保存这些参数、变量以及相关的寄存器，在需要恢复调用前再将保存的信息恢复。在<code>Linux</code>中可以调用<code>ucontext</code>族函数<code>getcontext</code>、<code>setcontext</code>等实现<sup><a href="#ref_12">[12]</a></sup>；<code>Boost</code>库也有类似的实现<code>Boost.Context</code><sup><a href="#ref_13">[13]</a></sup>；甚至可以自己通过汇编实现，例如微信的<code>libco</code><sup><a href="#ref_14">[14]</a></sup>。</p>
<p>无栈协程不需要此过程。 在《使用 C 语言实现协程》<sup><a href="#ref_15">[15]</a></sup>文章作者详细介绍了一种利用达夫设备<sup><a href="#ref_16">[16]</a></sup>原理使用C语言实现协程的方法。由于原理比较简单在此粗略的介绍下其核心代码：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c++" data-lang="c++"><span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">void</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#204a87;font-weight:bold">static</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">i</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#204a87;font-weight:bold">switch</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">state</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#204a87;font-weight:bold">case</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#ce5c00;font-weight:bold">:</span>               <span style="color:#8f5902;font-style:italic">// 函数开始执行
</span><span style="color:#8f5902;font-style:italic"></span>      <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">i</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000">i</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000">i</span><span style="color:#ce5c00;font-weight:bold">++</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
          <span style="color:#000">state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>        <span style="color:#8f5902;font-style:italic">// 我们会回到 &#34;case 1&#34; 的地方
</span><span style="color:#8f5902;font-style:italic"></span>          <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">i</span><span style="color:#000;font-weight:bold">;</span>
          <span style="color:#204a87;font-weight:bold">case</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#000;font-weight:bold">;</span>          <span style="color:#8f5902;font-style:italic">// 从上一次返回之后的地方开始执行
</span><span style="color:#8f5902;font-style:italic"></span>      <span style="color:#000;font-weight:bold">}</span>
  <span style="color:#000;font-weight:bold">}</span>
  <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">i</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><center><div style="color:orange; display: inline-block; color: #999; padding: 2px;">代码6</div></center></br>
这段奇怪的代码之所以能够正确执行是因为C语言标准规定：“在switch控制语句内，条件标号（case）可以出现在任意子语句之前，充作其前缀” <sup>[[16]](#ref_16)</sup>。现在分析下代码：
<p>第1次调用<code>function()</code>时, state值为0，所以执行case 0，然后执行for循环体，i赋0， state赋1(相当于保存状态，暂停执行)，函数返回i(0)。</br>
第2次调用<code>function()</code>时, state值为1，执行case 1（相当于恢复状态，继续执行），执行for循环后面的语句，i++， state赋1，函数返回i(1)。</br>
第3次调用<code>function()</code>时, state值为1，执行case 1，执行for循环后面的语句，i++， state赋1，函数返回i(2)。</br>
&hellip;</p>
<p>第10次调用<code>function()</code>时, state值为1，执行case 1，执行for循环后面的语句，i++， state赋1，函数返回i(9)。</br>
第11次调用<code>function()</code>时, state值为1，执行case 1，因为i&gt;=10所以跳出循环，函数返回i(9)。</br></p>
<p>由此可见，通过一个状态量，便可改变每次函数的执行流程。看到这里也许你会有疑问，为什么要用这么复杂的语法，if-else不也可以实现吗？其实这样写主要方便利用宏进行代码封装，具体的做法可见参考文献<sup><a href="#ref_15">[15]</a></sup>。其实在C++协程标准的实现里面也有类似的做法，只不过是编译器通过汇编指令直接跳转实现的，具体的分析后面会介绍。</p>
</li>
<li>
<p>它们通过返回到调用方暂停执行。这里说的是协程的非对称性，协程按照调度方式可以分为对称协程和非对称协程<sup><a href="#ref_17">[17]</a></sup>。</p>
<ul>
<li>对称协程 Symmetric Coroutine：任何一个协程都是相互独立且平等的，调度权可以在任意协程之间转移。</li>
<li>非对称协程 Asymmetric Coroutine：协程出让调度权的目标只能是它的调用者，即协程之间存在调用和被调用关系。</li>
</ul>
<p>直白的说就是， 对称协程只提供了一种类似于goto的操作将控制权直接传递给其它指定的协程，而非对称协程需要前面所说的“暂停”和“恢复”两种操作。对于对称协程调用者和被调用者之间的关系是平等的，他们甚至可以互相调用，可以做到A-&gt;B-&gt;C-&gt;A（A,B,C为不同的协程函数）这样的链式调用。非对称协程具有明确的层级关系被调用者必须先返回到调用者再调用其他协程，如A-&gt;B-&gt;A-&gt;C-&gt;A。粗看好像对称协程更容易使用，因为其具有更大的自由度，但是它和goto遇到的问题是一样的，在一个复杂的系统里过多的自由跳转会破坏代码的结构，让程序逻辑不容易得到把控。例外对称协程只是非对称协程的一个特例，我们可以通过添加一个中立的第三方调度中心的方式将非对称协程转换成对称协程（只需要在所有协程“暂停”时将控制权转交给调度中心统一调度即可）。</p>
</li>
<li>
<p>并且从栈分离存储恢复所要求的数据。这里所说是相对于普通的函数调用，如图1，图2所示，我们在调用函数时总是会先将调用参数和临时变量以及一些相关寄存器信息压入堆栈，在后面的指令需要时再从堆栈中取出，最后函数执行完后清理掉。但是协程具有额外的“暂停”和“恢复”操作，如果还是使用堆栈上的信息，根据堆栈的使用规则，协程“恢复”后堆栈已经遭到破坏，因为“暂停”后为了使调用者能够正常工作，我们必须保证此时的堆栈状态能够恢复到调用者的堆栈状态，“恢复”后由于协程的堆栈信息已经丢失，我们无法再使用协程的堆栈。所以我们需要额外的手段将这些信息存储在其他地方（一般是在堆上）。值得注意的是这里的用词是“分离”而不是复制，具体实现后面做详细的分析。</p>
</li>
<li>
<p>这允许编写异步执行的顺序代码（例如不使用显式的回调来处理非阻塞 I/O）。 参考前面代码4的例子。</p>
</li>
<li>
<p>还支持对惰性计算的无限序列上的算法及其他用途。</p>
<ul>
<li>
<p>无限序列（infinite sequence）是指一列或一串离散的数字能够与另一组正整数$\{0, 1, 2, 3, &hellip;\}$相比而形成一定的联系和概念。例如无限序列 $N=(0, 1, 2, 3, &hellip;)$ 和 $S＝(1, \frac{1}{2}, \frac{1}{4}, \frac{1}{8}, &hellip;, \frac{1}{2^n}, &hellip;)$, 当然斐波纳契数列$F＝(1, 1, 2, 3, 5, 8, &hellip;, n_{i-1}, n_{i}, n_{i-1} + n_{i}, &hellip;)$也是一种无限序列。</p>
</li>
<li>
<p>惰性计算（又称延迟求值）在科学计算和函数式编程中经常用到的一种优化手段。引用<code>Wiki</code>中给的例子简单说明下， 下面是部分代码<sup><a href="#ref_19">[19]</a></sup>:</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#000">matrix</span> <span style="color:#000">A</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">B</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000">A</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">][</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">]</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">101</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000">B</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">][</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">]</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">102</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000">matrix_add</span> <span style="color:#000">R</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">A</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000">B</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">R</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">][</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">];</span>
</code></pre></div><p>上面的代码是计算两个矩阵（A, B）相加后得到新矩阵(R)某个索引的值。通常我们在执行完<code>R=A+B</code>时便会算出R中的所有元素，因为A和B均有$12=3\times4$个元素，所以为了得到R需要经过12次加法运算，但是我们只需要<code>R[3][4]</code>这一个元素的值，所以我们浪费了11次运算。惰性计算的方法是执行到<code>R=A+B</code>时，我们并不去计算，而只是存储计算所需要的数据，计算被延迟到了取<code>R[3][4]</code>值时才开始。完整的代码请参考<code>Wiki</code><sup><a href="#ref_19">[19]</a></sup>。</p>
</li>
<li>
<p>我们通过<code>cppcoro</code>计算斐波纳契的例子来说明下C++协程是怎么实现惰性计算的。</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#000">cppcoro</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">generator</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#204a87;font-weight:bold">uint64_t</span><span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">fibonacci</span><span style="color:#000;font-weight:bold">()</span>
<span style="color:#000;font-weight:bold">{</span>
  <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#204a87;font-weight:bold">uint64_t</span> <span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">b</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#204a87;font-weight:bold">while</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">true</span><span style="color:#000;font-weight:bold">)</span>
  <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#204a87;font-weight:bold">co_yield</span> <span style="color:#000">b</span><span style="color:#000;font-weight:bold">;</span>                     <span style="color:#8f5902;font-style:italic">// 2
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#204a87;font-weight:bold">auto</span> <span style="color:#000">tmp</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">a</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">b</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">b</span> <span style="color:#ce5c00;font-weight:bold">+=</span> <span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">}</span>
<span style="color:#000;font-weight:bold">}</span>

<span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">usage</span><span style="color:#000;font-weight:bold">()</span>
<span style="color:#000;font-weight:bold">{</span>
  <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">auto</span> <span style="color:#f57900">i</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">fibonacci</span><span style="color:#000;font-weight:bold">())</span>         <span style="color:#8f5902;font-style:italic">// 1
</span><span style="color:#8f5902;font-style:italic"></span>  <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">i</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#0000cf;font-weight:bold">1&#39;000&#39;000</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">break</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">cout</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">i</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">endl</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">}</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><p>在代码注释1处，我们通过循环不停调用fibonacci， 第一次调用fibonacci时从函数入口开始初始化a, b，然后进入循环，执行 <code>co_yield b; </code>这是会从fibonacci函数回到usage，执行for循环体中代码，再次调用fibonacci时实际上执行的是fibonacci协程的的resume方法，所以再次进入fibonacci函数时并不是从入口重新执行，而是恢复到co_yield后面的语句<code>auto tmp = a;</code>继续执行算出数列的下个值b，然后循环再次<code>co_yield</code>回到usage，如此循环往复，最终得到数列中第1'000'000个值。 </br>
可以看到第一次调用时我们并没有把fibonacci数列中的前1'000'000个值全部计算出来，而是把计算推迟到了后面的调用。如果有足够的时间和算力我们可以计算任意数列的值，从算法上并不限制数列的长度，可以无限的计算下去， 所以说是“无限序列”。</p>
</li>
</ul>
</li>
<li>
<p>若函数的定义做下列任何内容之一，则它是协程。 简单的说就是如果一个函数中出现co_await、co_yield、co_return 三个关键字，你就可以认为它是一个协程函数。</p>
</li>
</ul>
<h2 id="原理剖析">原理剖析</h2>
<p><code>C++</code>语言的难学已经是恶名远播，提起C++时许多程序员（甚至是学C语言的程序员）第一反应就是“这是个令人胆颤、难缠、恶心的家伙”。其实换个角度<code>C</code>与<code>C++</code>的关系非常类似于<code>JavaScript</code>和<code>Typescript</code>(如果你用过应该很好理解)，C++作为C的超集经过编译器的转换绝大部分的代码是可以转换成C，因为有了编译器的帮助C++可以实现许多高级的封装，每次编译时编译器都会根据需要向用户编写的代码里“偷偷”加料。例如构造析构函数的自动创建、虚函数表、this指针传递等等，对于这方面的知识建议大家看看《深入了解c++内核对象模型》这本书。因为编译器的这些小动作对用户是无感知的，这就造成了用户对C++一些行为表现的困惑，也加大了大家学习C++的难度。而协程特性是我目前见到C++让编译器加料最多的一次，理所当然理解的难度指数也是陡增。现在就让我们细细的分析下编译器给我们加的这些料的配方吧！</p>
<h3 id="协程函数展开">协程函数展开</h3>
<hr>
<p>让我们看个最简单的例子，看看<code>main</code>函数调用<code>remote_query_all</code>时发生了什么。</p>
<p>我们先定义个函数返回对象的类型<code>return_ignore</code>，为什么要定义它以及怎么定义我们后面介绍，提前申明只是不想干扰后面的分析，如果不清楚具体的含义就先无视它。</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">return_ignore</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">promise_type</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">return_ignore</span> <span style="color:#000">get_return_object</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">return_ignore</span><span style="color:#000;font-weight:bold">{};</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">auto</span> <span style="color:#000">initial_suspend</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">experimental</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">suspend_always</span><span style="color:#000;font-weight:bold">{};</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">auto</span> <span style="color:#000">final_suspend</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">experimental</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">suspend_never</span><span style="color:#000;font-weight:bold">{};</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">unhandled_exception</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">return_void</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{}</span>
  <span style="color:#000;font-weight:bold">};</span>
<span style="color:#000;font-weight:bold">};</span>
</code></pre></div><p>下面是我们现在需要关注的代码，为了排除干扰我已经做了最大程度的精简。test_coroutine的模板参数其实是不必要的，但是为了通用性还是加上了。</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#8f5902;font-style:italic">#include</span> <span style="color:#8f5902;font-style:italic">&lt;experimental/coroutine&gt;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>
<span style="color:#204a87;font-weight:bold">template</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">typename</span> <span style="color:#000;font-weight:bold">...</span><span style="color:#000">Args</span><span style="color:#ce5c00;font-weight:bold">&gt;</span>
<span style="color:#000">return_ignore</span> <span style="color:#000">test_coroutine</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">Args</span><span style="color:#000;font-weight:bold">...</span> <span style="color:#000">args</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#204a87;font-weight:bold">co_await</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">experimental</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">suspend_always</span><span style="color:#000;font-weight:bold">{};</span>
<span style="color:#000;font-weight:bold">}</span>

<span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#000">test_coroutine</span><span style="color:#000;font-weight:bold">();</span>

  <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><p>下面我们看看编译器展开test_coroutine函数后的代码成什么样子， Gor Nishanov和luncliff对其做了精彩的分析，推荐看看他们的报告<sup><a href="#ref_21">[21]</a></sup><sup><a href="#ref_22">[22]</a></sup>，下面是我结合他们俩的报告整理出来的全部展开：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#204a87;font-weight:bold">template</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">typename</span> <span style="color:#000;font-weight:bold">...</span><span style="color:#000">Args</span><span style="color:#ce5c00;font-weight:bold">&gt;</span>
<span style="color:#000">return_ignore</span> <span style="color:#000">test_coroutine</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">Args</span><span style="color:#000;font-weight:bold">...</span> <span style="color:#000">args</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#204a87;font-weight:bold">using</span> <span style="color:#000">T</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">coroutine_traits</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">return_type</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">Args</span><span style="color:#000;font-weight:bold">...</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#204a87;font-weight:bold">using</span> <span style="color:#000">promise_type</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">T</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">promise_type</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#204a87;font-weight:bold">using</span> <span style="color:#000">frame_type</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">tuple</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">frame_prefix</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">promise_type</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">Args</span><span style="color:#000;font-weight:bold">...</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#204a87;font-weight:bold">auto</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">frame</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">frame_type</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000">promise_type</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#204a87;font-weight:bold">operator</span> <span style="color:#204a87;font-weight:bold">new</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">frame_type</span><span style="color:#000;font-weight:bold">));</span>
  <span style="color:#204a87;font-weight:bold">auto</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">p</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">addressof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">get</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">frame</span><span style="color:#000;font-weight:bold">));</span> 
  <span style="color:#000">return_ignore</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">__return_object</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">__return_object</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">p</span><span style="color:#ce5c00;font-weight:bold">-&gt;</span><span style="color:#000">get_return_object</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">};</span>
  
  <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#8f5902;font-style:italic">// co_await p-&gt;initial_suspend();
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#204a87;font-weight:bold">auto</span><span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">tmp</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">p</span><span style="color:#ce5c00;font-weight:bold">-&gt;</span><span style="color:#000">initial_suspend</span><span style="color:#000;font-weight:bold">();</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">await_ready</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#000">__builtin_coro_save</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#8f5902;font-style:italic">// frame-&gt;suspend_index = n;
</span><span style="color:#8f5902;font-style:italic"></span>      <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">await_suspend</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">coroutine_handle</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#000">__builtin_coro_suspend</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#8f5902;font-style:italic">// jmp 
</span><span style="color:#8f5902;font-style:italic"></span>      <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#f57900">resume_label_n</span><span style="color:#000;font-weight:bold">:</span>
      <span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">await_resume</span><span style="color:#000;font-weight:bold">();</span>
  <span style="color:#000;font-weight:bold">}</span>
  

  <span style="color:#204a87;font-weight:bold">try</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#8f5902;font-style:italic">// co_await std::experimental::suspend_always{};
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#204a87;font-weight:bold">auto</span><span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">tmp</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">experimental</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">suspend_always</span><span style="color:#000;font-weight:bold">{};</span>
      <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">await_ready</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#000">__builtin_coro_save</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#8f5902;font-style:italic">// frame-&gt;suspend_index = m;
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">await_suspend</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">coroutine_handle</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#000;font-weight:bold">{</span>
          <span style="color:#000">__builtin_coro_suspend</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#8f5902;font-style:italic">// jmp 
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#000;font-weight:bold">}</span>
      <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#f57900">resume_label_m</span><span style="color:#000;font-weight:bold">:</span>
      <span style="color:#000">p</span><span style="color:#ce5c00;font-weight:bold">-&gt;</span><span style="color:#000">return_void</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">await_resume</span><span style="color:#000;font-weight:bold">());</span>
      <span style="color:#8f5902;font-style:italic">// p-&gt;return_value(tmp.await_resume());        
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000;font-weight:bold">}</span>

    
    <span style="color:#204a87;font-weight:bold">goto</span> <span style="color:#000">__final_suspend_point</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">}</span> <span style="color:#204a87;font-weight:bold">catch</span> <span style="color:#000;font-weight:bold">(...)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">p</span><span style="color:#ce5c00;font-weight:bold">-&gt;</span><span style="color:#000">unhandled_exception</span><span style="color:#000;font-weight:bold">();</span>
  <span style="color:#000;font-weight:bold">}</span>

  <span style="color:#f57900">__final_suspend_point</span><span style="color:#000;font-weight:bold">:</span>
    <span style="color:#204a87;font-weight:bold">co_await</span> <span style="color:#000">p</span><span style="color:#ce5c00;font-weight:bold">-&gt;</span><span style="color:#000">final_suspend</span><span style="color:#000;font-weight:bold">();</span>

  <span style="color:#f57900">__destroy_point</span><span style="color:#000;font-weight:bold">:</span>
    <span style="color:#000">promise_type</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#204a87;font-weight:bold">operator</span> <span style="color:#204a87;font-weight:bold">delete</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">frame</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">frame_type</span><span style="color:#000;font-weight:bold">));</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><p>哈哈，惊不惊喜？意不意外？你的一行代码，编译器给你加了这么多东西。上面的代码对理解协程非常重要，它就像导航地图里面的预览图一样，在你学习协程的各种知识和概念是不被绕晕。我在刚开始学习协程时就因为缺乏对全局的理解，导致当面对一大堆新的概念时不说其它的，心理上就有莫大压力，想理清它们关系也无所适从，希望上面的代码能够帮助到你。</p>
<hr>
<p><strong>注意</strong></p>
<p>上面的代码并非真实的代码，只是根据文献和逆向推导而来，而且各个编译器厂商实现也有差异，这里列出的代码只是帮助大家理解而已。</p>
<hr>
<p>下面我们对照着上面的代码说说协程的几个重要概念。</p>
<h3 id="co_await-表达式">co_await 表达式</h3>
<p>co_await 表达式， 就是我们在co_await关键词后面的运算对象(operand)，这个运算对象必须满足特定的规则：必须包含<code>await_ready</code>、<code>await_suspend</code>、<code>await_resume</code>三个函数。通过展开的代码我们很容易理解为什么做这样的规定。例如我们前面例子中<code>std::experimental::suspend_always</code>的定义是这样的：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">suspend_always</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#204a87;font-weight:bold">bool</span> <span style="color:#000">await_ready</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#204a87;font-weight:bold">noexcept</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87">false</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">await_suspend</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">coroutine_handle</span><span style="color:#ce5c00;font-weight:bold">&lt;&gt;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">noexcept</span> <span style="color:#000;font-weight:bold">{}</span>

    <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">await_resume</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#204a87;font-weight:bold">noexcept</span> <span style="color:#000;font-weight:bold">{}</span>
<span style="color:#000;font-weight:bold">};</span>
</code></pre></div><p>co_await 表达式展开后是这样的：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#8f5902;font-style:italic">// co_await std::experimental::suspend_always{};
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000;font-weight:bold">{</span>
  <span style="color:#204a87;font-weight:bold">auto</span><span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">tmp</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">experimental</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">suspend_always</span><span style="color:#000;font-weight:bold">{};</span>
  <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">await_ready</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">__builtin_coro_save</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#8f5902;font-style:italic">// frame-&gt;suspend_index = m;
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">await_suspend</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">coroutine_handle</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#000">__builtin_coro_suspend</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#8f5902;font-style:italic">// jmp 
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#000;font-weight:bold">}</span>
  <span style="color:#000;font-weight:bold">}</span>

<span style="color:#f57900">resume_label_m</span><span style="color:#000;font-weight:bold">:</span>
  <span style="color:#000">tmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">await_resume</span><span style="color:#000;font-weight:bold">();</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><p>可以看到展开后直接调用了运算对象的<code>await_ready</code>、<code>await_suspend</code>、<code>await_resume</code>三个方法。那么这三个函数式作什么用的呢？</p>
<ul>
<li><code>await_ready</code>: 在暂停协程前给了一次机会，判断是否可以避免异步调用以达到节省暂停带来的开销。</li>
<li><code>await_suspend</code>：可以看做是暂停前的准备，通常我们在这个函数中分配任务系统，线程去完成异步完成，然后进入暂停状态。注意这个函数有一个非常重要的参数<code>coroutine_handle</code>(协程句柄)，通过这个句柄我们可以操控协程的状态，通常我们需要将这个句柄保存起来，在适当的时机（异步任务完成时）通过它恢复当前协程。</li>
<li><code>await_resume</code>：这个很好理解，协程恢复后执行的代码，值得注意的是恢复后而不是回复前。</li>
</ul>
<h3 id="承诺promise对象">承诺（promise）对象</h3>
<p>编译器用 std::coroutine_traits 从协程的返回类型确定 Promise 类型<sup><a href="#ref_8">[8]</a></sup>。也就是说编译器会根据你的协程函数放回值类型去确定Promise类型，确定方法就是看你的返回类型有没有promise_type的定义，比如return_ignore的Promise类型就是return_ignore::promise_type。这个Promise类型类似于std::promise，应为是异步所以无法立即给你结果，但是我承诺你将来你可以向我申请查询结果。同样通过展开代码我们可以看到编译器调用了Promise的许多方法，所以我们在定义promise_type时必须符合编译的规范和需求。下面是promise_type必须具有的接口：</p>
<ul>
<li>
<p><code>get_return_object</code>: 按照规定该调用的结果将在协程首次暂停时返回给调用方<sup><a href="#ref_8">[8]</a></sup>。调用者可以通过此函数返回的对象获得协程函数最终的结果。可以参考<code>return_ignore</code>的代码，这里需要注意的是我们不能将结果保存在<code>promise_type</code>里，因为协程函数结束时会析构这个对象，保存的数据会丢失。可选的方案是在<code>promise_type</code>中保存返回对象然后在返回去，如下面的代码，但是返回值类型是个问题，如果返回_ret指针，<code>promise_type</code>析构后_ret一样会被收回，返回对象会多出一份数据，return_value函数操作的还不是同一个对象, 所以_value只能是个指针类型。还有个小的问题是直接在内部类声明外面的类对象会导致类型未定义的错误，只能放到类定义外面定义内部类。</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">return_object</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">promise_type</span><span style="color:#000;font-weight:bold">;</span>

  <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">get_ret</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">_value</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">}</span>

<span style="color:#204a87;font-weight:bold">private</span><span style="color:#ce5c00;font-weight:bold">:</span>
  <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">shared_ptr</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">int</span><span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">_value</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">make_shared</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">int</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">);</span>
<span style="color:#000;font-weight:bold">};</span>

<span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">return_object</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">promise_type</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#000">return_object</span> <span style="color:#000">get_return_object</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">_ret</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">}</span>

  <span style="color:#000;font-weight:bold">...</span>

  <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">return_value</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">value</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">_ret</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_value</span> <span style="color:#ce5c00;font-weight:bold">=</span>  <span style="color:#000">value</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">}</span>

  <span style="color:#000">return_object</span> <span style="color:#000">_ret</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">};</span>
</code></pre></div><p>另一个方案是在promise_type类中提供个方法让返回对象将this指针传给promise_type类，相当于来了个回马枪：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp"><span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">return_object</span> <span style="color:#000;font-weight:bold">{</span>
  <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">promise_type</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">return_object</span> <span style="color:#000">get_return_object</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">return_object</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">);</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">set_return_object</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">return_object</span><span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#000">ret</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">_ret</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">ret</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">auto</span> <span style="color:#000">initial_suspend</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">experimental</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">suspend_never</span><span style="color:#000;font-weight:bold">{};</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">auto</span> <span style="color:#000">final_suspend</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">experimental</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">suspend_never</span><span style="color:#000;font-weight:bold">{};</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">unhandled_exception</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">return_value</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">value</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
      <span style="color:#000">_ret</span><span style="color:#ce5c00;font-weight:bold">-&gt;</span><span style="color:#000">_value</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">value</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000">return_object</span><span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#000">_ret</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">nullptr</span><span style="color:#000;font-weight:bold">;</span>
  <span style="color:#000;font-weight:bold">};</span>

  <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">get_ret</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">_value</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">}</span>

<span style="color:#204a87;font-weight:bold">private</span><span style="color:#ce5c00;font-weight:bold">:</span>
  <span style="color:#000">return_object</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">promise_type</span><span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#000">p</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">p</span><span style="color:#ce5c00;font-weight:bold">-&gt;</span><span style="color:#000">set_return_object</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">);</span> <span style="color:#000;font-weight:bold">}</span>
  <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">_value</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">};</span>
</code></pre></div></li>
<li>
<p><code>initial_suspend</code>: 控制是否在进入协程函数前先暂停协程。</p>
</li>
<li>
<p><code>initial_suspend</code>: 做些收尾工作。</p>
</li>
<li>
<p><code>unhandled_exception</code>: 参考展开代码，捕获到异常时调用此方法。</p>
</li>
<li>
<p><code>return_value</code>, <code>return_void</code>: 这两个函数根据协程函数内的<code>co_return</code>表达式二选一即可，协程函数遇到<code>co_return</code>会根据表达式内容调用这两个函数之一，并将结果传递给放回对象。</p>
</li>
</ul>
<h3 id="协程句柄-coroutine-handle">协程句柄 (coroutine handle)</h3>
<p>这是用于恢复协程执行或销毁协程帧的非拥有柄。前面的“承诺对象”是从内部控制协程，将异常和结果传递给系统外部，而协程句柄正好相反，它提供了从外部操控协程的方法。先看下结构声明：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-cpp" data-lang="cpp">   <span style="color:#204a87;font-weight:bold">template</span> <span style="color:#ce5c00;font-weight:bold">&lt;&gt;</span>
    <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">coroutine_handle</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">void</span><span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#8f5902;font-style:italic">// no promise access
</span><span style="color:#8f5902;font-style:italic"></span>
        <span style="color:#204a87;font-weight:bold">static</span> <span style="color:#000">coroutine_handle</span> <span style="color:#000">from_address</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">void</span><span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#000">_Addr</span><span style="color:#000;font-weight:bold">);</span>

        <span style="color:#204a87;font-weight:bold">void</span><span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#000">address</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#204a87;font-weight:bold">noexcept</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">operator</span><span style="color:#000;font-weight:bold">()()</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#204a87;font-weight:bold">noexcept</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#000">resume</span><span style="color:#000;font-weight:bold">();</span>
        <span style="color:#000;font-weight:bold">}</span>

        <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">resume</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#000">_coro_resume</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">_Ptr</span><span style="color:#000;font-weight:bold">);</span>
        <span style="color:#000;font-weight:bold">}</span>

        <span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">destroy</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#000">_coro_destroy</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">_Ptr</span><span style="color:#000;font-weight:bold">);</span>
        <span style="color:#000;font-weight:bold">}</span>

        <span style="color:#204a87;font-weight:bold">bool</span> <span style="color:#000">done</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">_Ptr</span><span style="color:#ce5c00;font-weight:bold">-&gt;</span><span style="color:#000">_Index</span> <span style="color:#ce5c00;font-weight:bold">==</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">}</span>

        <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">_Resumable_frame_prefix</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#204a87;font-weight:bold">using</span> <span style="color:#000">_Resume_fn</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">void</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">__cdecl</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000;font-weight:bold">)(</span><span style="color:#204a87;font-weight:bold">void</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000;font-weight:bold">);</span>

            <span style="color:#000">_Resume_fn</span> <span style="color:#000">_Fn</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">uint16_t</span> <span style="color:#000">_Index</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">uint16_t</span> <span style="color:#000">_Flags</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">};</span>

    <span style="color:#204a87;font-weight:bold">protected</span><span style="color:#ce5c00;font-weight:bold">:</span>
        <span style="color:#000">_Resumable_frame_prefix</span><span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#000">_Ptr</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">nullptr</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">};</span>
</code></pre></div><p>在协程句柄中我们看到一个非常重要的方法<code>resume</code>和一个<code>frame_prefix</code>的成员变量，<code>resume</code>方法应该不用多说了，调用它可以将协程从“暂停”的地方“恢复”。有意思的是<code>_Ptr</code>这个指针，它的类型是<code>_Resumable_frame_prefix</code>, 其中包括一个函数指针<code>_Fn</code>和两个状态值。<code>_Fn</code>其实就是协程跳转后真正执行的函数地址，它通过和<code>_Index</code>配合能够跳转到协程暂停后的指令地址。具体的分析参考后面的<a href="%E5%8D%8F%E7%A8%8B%E8%B7%B3%E8%BD%AC">协程跳转</a>相关内容。</p>
<h3 id="协程状态-coroutine-state">协程状态 (coroutine state)</h3>
<p>可以把协程状态理解为为使协程正常工作在堆上申请分配的内存，这里面包括前面说过的承诺对象、参数、临时变量和协程句柄里面记录的<code>_Resumable_frame_prefix</code>内容。</p>
<h3 id="协程跳转">协程跳转</h3>
<p>在协程展开代码中还有两个函数<code>__builtin_coro_save</code>和<code>__builtin_coro_suspend</code>，这其实是编译器内置的代码，在msvc的头文件experimental\resumable中你可以看到许多以<code>_coro_</code>开头的类似函数，这些函数式协程实现的基础。下面我们以一个简单的例子，通过跟踪汇编代码来看下编译器是怎么实现协程的“暂停”和“恢复”操作的。</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-CPP" data-lang="CPP"><span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">experimental</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">coroutine_handle</span><span style="color:#ce5c00;font-weight:bold">&lt;&gt;</span> <span style="color:#000">g_h</span><span style="color:#000;font-weight:bold">;</span>

<span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">awaitee</span> <span style="color:#000;font-weight:bold">{</span>
	<span style="color:#204a87;font-weight:bold">bool</span> <span style="color:#000">await_ready</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#204a87;font-weight:bold">noexcept</span> <span style="color:#000;font-weight:bold">{</span>
		<span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87">false</span><span style="color:#000;font-weight:bold">;</span>
	<span style="color:#000;font-weight:bold">}</span>

	<span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">await_suspend</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">experimental</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">coroutine_handle</span><span style="color:#ce5c00;font-weight:bold">&lt;&gt;</span> <span style="color:#000">h</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">noexcept</span> <span style="color:#000;font-weight:bold">{</span>
		<span style="color:#000">g_h</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">h</span><span style="color:#000;font-weight:bold">;</span>
	<span style="color:#000;font-weight:bold">}</span>

	<span style="color:#204a87;font-weight:bold">void</span> <span style="color:#000">await_resume</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#204a87;font-weight:bold">noexcept</span> <span style="color:#000;font-weight:bold">{}</span>
<span style="color:#000;font-weight:bold">};</span>

<span style="color:#204a87;font-weight:bold">template</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">typename</span> <span style="color:#000;font-weight:bold">...</span><span style="color:#000">Args</span><span style="color:#ce5c00;font-weight:bold">&gt;</span>
<span style="color:#000">return_object</span> <span style="color:#000">test_coroutine</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">Args</span><span style="color:#000;font-weight:bold">...</span> <span style="color:#000">args</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
	<span style="color:#204a87;font-weight:bold">co_await</span> <span style="color:#000">awaitee</span><span style="color:#000;font-weight:bold">();</span>

	<span style="color:#204a87;font-weight:bold">co_return</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>

<span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
	<span style="color:#204a87;font-weight:bold">auto</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">ret</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">test_coroutine</span><span style="color:#000;font-weight:bold">();</span>
	<span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">ret</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">get_ret</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#ce5c00;font-weight:bold">==</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
		<span style="color:#000">g_h</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">resume</span><span style="color:#000;font-weight:bold">();</span>
	<span style="color:#000;font-weight:bold">}</span>

	<span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">cout</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#4e9a06">&#34;coroutine ended.&#34;</span>  <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">ret</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">get_ret</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#ce5c00;font-weight:bold">&lt;&lt;</span> <span style="color:#000">std</span><span style="color:#ce5c00;font-weight:bold">::</span><span style="color:#000">endl</span><span style="color:#000;font-weight:bold">;</span>
	<span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span>
</code></pre></div><div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-asm" data-lang="asm"><span style="color:#8f5902;font-style:italic">; test_coroutine
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000">xor</span>         <span style="color:#000">edi</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">edi</span>  
<span style="color:#000">mov</span>         <span style="color:#000">ecx</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">100</span><span style="color:#000">h</span>  
<span style="color:#000">add</span>         <span style="color:#000">rcx</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">28</span><span style="color:#000">h</span>  
<span style="color:#000">call</span>        <span style="color:#000">operator</span> <span style="color:#000">new</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">07</span><span style="color:#000">FF6282C10CDh</span><span style="color:#000;font-weight:bold">)</span>                 <span style="color:#8f5902;font-style:italic">; 在堆上分配协程帧内存
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000">lea</span>         <span style="color:#000">r9</span><span style="color:#000;font-weight:bold">,[</span><span style="color:#000">a</span><span style="color:#000;font-weight:bold">]</span>                                        <span style="color:#a40000">；向</span><span style="color:#000">_InitCoro</span> <span style="color:#a40000">传递</span> <span style="color:#000">test_coroutine</span> <span style="color:#a40000">参数</span> <span style="color:#000">a</span>
<span style="color:#000">mov</span>         <span style="color:#000">dword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rsp</span><span style="color:#a40000">+</span><span style="color:#0000cf;font-weight:bold">170</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">edi</span>  
<span style="color:#000">mov</span>         <span style="color:#000">r8</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">rbx</span>  
<span style="color:#000">lea</span>         <span style="color:#000">rcx</span><span style="color:#000;font-weight:bold">,[</span><span style="color:#000">rsp</span><span style="color:#a40000">+</span><span style="color:#0000cf;font-weight:bold">170</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">]</span>  
<span style="color:#000">lea</span>         <span style="color:#000">rdx</span><span style="color:#000;font-weight:bold">,[</span><span style="color:#000">rax</span><span style="color:#a40000">+</span><span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">]</span>                                 <span style="color:#8f5902;font-style:italic">; 计算协程句柄地址
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000">lea</span>         <span style="color:#000">rax</span><span style="color:#000;font-weight:bold">,[</span><span style="color:#000">b</span><span style="color:#000;font-weight:bold">]</span>                                       <span style="color:#a40000">；向</span><span style="color:#000">_InitCoro</span> <span style="color:#a40000">传递</span> <span style="color:#000">test_coroutine</span> <span style="color:#a40000">参数</span> <span style="color:#000">b</span>
<span style="color:#000">mov</span>         <span style="color:#000">qword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rsp</span><span style="color:#a40000">+</span><span style="color:#0000cf;font-weight:bold">20</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">rax</span>  
<span style="color:#000">call</span>        <span style="color:#000">test_coroutine$_InitCoro$1</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">07</span><span style="color:#000">FF6282C1630h</span><span style="color:#000;font-weight:bold">)</span>   <span style="color:#8f5902;font-style:italic">; 调用初始化协程的内置函数
</span></code></pre></div><p>请在后面注意这里的内存变化：
<img src="https://i.loli.net/2020/10/14/7PvtWDedrpn9z2s.png" alt="create_coroutine_frame.png"></p>
<p>这里值得一提的是计算协程句柄地址中的偏移地址<code>10h</code>, 这个值怎么来的呢？ 翻看msvc中resumable头文件，可以看到coroutine_handle的定义：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-CPP" data-lang="CPP"><span style="color:#204a87;font-weight:bold">template</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">typename</span> <span style="color:#000">_PromiseT</span><span style="color:#ce5c00;font-weight:bold">&gt;</span>
<span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">coroutine_handle</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">coroutine_handle</span><span style="color:#ce5c00;font-weight:bold">&lt;&gt;</span> <span style="color:#000;font-weight:bold">{</span> 

    <span style="color:#204a87;font-weight:bold">static</span> <span style="color:#000">coroutine_handle</span> <span style="color:#000">from_promise</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">_PromiseT</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">_Prom</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">noexcept</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#204a87;font-weight:bold">auto</span> <span style="color:#000">_FramePtr</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">reinterpret_cast</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">char</span><span style="color:#ce5c00;font-weight:bold">*&gt;</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">_STD</span> <span style="color:#000">addressof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">_Prom</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">_ALIGNED_SIZE</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">coroutine_handle</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">_PromiseT</span><span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#000">_Result</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">_Result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_Ptr</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">reinterpret_cast</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">_Resumable_frame_prefix</span><span style="color:#ce5c00;font-weight:bold">*&gt;</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">_FramePtr</span><span style="color:#000;font-weight:bold">);</span>
        <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">_Result</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>

    <span style="color:#000;font-weight:bold">...</span>
    
    <span style="color:#204a87;font-weight:bold">static</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">size_t</span> <span style="color:#000">_ALIGN_REQ</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">void</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#204a87;font-weight:bold">static</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">size_t</span> <span style="color:#000">_ALIGNED_SIZE</span> <span style="color:#ce5c00;font-weight:bold">=</span>
        <span style="color:#000">is_empty_v</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">_PromiseT</span><span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">((</span><span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">_PromiseT</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">_ALIGN_REQ</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#ce5c00;font-weight:bold">~</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">_ALIGN_REQ</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">));</span>

    <span style="color:#000">_PromiseT</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">promise</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#204a87;font-weight:bold">noexcept</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#204a87;font-weight:bold">const_cast</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">_PromiseT</span><span style="color:#ce5c00;font-weight:bold">*&gt;</span><span style="color:#000;font-weight:bold">(</span>
            <span style="color:#204a87;font-weight:bold">reinterpret_cast</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">_PromiseT</span> <span style="color:#204a87;font-weight:bold">const</span><span style="color:#ce5c00;font-weight:bold">*&gt;</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">reinterpret_cast</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">char</span> <span style="color:#204a87;font-weight:bold">const</span><span style="color:#ce5c00;font-weight:bold">*&gt;</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">_Ptr</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#000">_ALIGNED_SIZE</span><span style="color:#000;font-weight:bold">));</span>
    <span style="color:#000;font-weight:bold">}</span>
<span style="color:#000;font-weight:bold">};</span>
</code></pre></div><p>从中分析可以知道promise地址（实际上就是协程栈的地址）与协程句柄的帧前缀地址是可以互相转换的，这两个地址之间相差了<code>_ALIGNED_SIZE</code>字节，所以得到一个地址相加或相减便能转换成另一个地址。那么<code>_ALIGNED_SIZE</code>是怎么算出来的呢？其实这是根据我们定义的promise_type的大小进行字节对齐（两倍的<code>sizeof(void*)</code>）得到的。例如我们定义的<code>return_object::promise_type</code>里面有一个指针成员变量，所以它的大小为8（64位机器）在和<code>sizeof(void*) * 2 == 16 == 10h</code>对齐后得到的结果便是16即10h, 如果我们在<code>promise_type</code>里面定义三个指针变量，大小变为24与16字节对齐后变为32即20h。</p>
<p>下面我们继续分析<code>_InitCoro</code>函数：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-CPP" data-lang="CPP"><span style="color:#000">mov</span>         <span style="color:#000">qword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rsp</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">rdx</span>                 <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">从前面的分析我们知道</span><span style="color:#000">rdx其实是协程句柄帧前缀的地址</span>
<span style="color:#000">sub</span>         <span style="color:#000">rsp</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">28</span><span style="color:#000">h</span>  
<span style="color:#000">mov</span>         <span style="color:#000">r11d</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">100</span><span style="color:#000">h</span>  
<span style="color:#000">mov</span>         <span style="color:#000">rax</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">qword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">b</span><span style="color:#000;font-weight:bold">]</span>                      <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">参数</span><span style="color:#000">b</span>
<span style="color:#000">mov</span>         <span style="color:#000">r10d</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">dword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rax</span><span style="color:#000;font-weight:bold">]</span>  
<span style="color:#000">mov</span>         <span style="color:#000">eax</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">dword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">r9</span><span style="color:#000;font-weight:bold">]</span>                      <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">参数</span><span style="color:#000">a</span>
<span style="color:#000">mov</span>         <span style="color:#000">dword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rdx</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000">r11</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">eax</span>             <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">加参数</span><span style="color:#000">a</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#a40000">存入协程栈</span>
<span style="color:#000">mov</span>         <span style="color:#000">dword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rdx</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000">r11</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">r10d</span>            <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">将参数</span><span style="color:#000">b</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#a40000">存入协程栈</span>
<span style="color:#000">lea</span>         <span style="color:#000">rax</span><span style="color:#000;font-weight:bold">,[</span><span style="color:#000">test_coroutine</span><span style="color:#a40000">$</span><span style="color:#000">_ResumeCoro</span><span style="color:#a40000">$</span><span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">07FF</span><span style="color:#0000cf;font-weight:bold">6282</span><span style="color:#000">C2FF0h</span><span style="color:#000;font-weight:bold">)]</span>  <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">协程函数入口地址</span>
<span style="color:#000">mov</span>         <span style="color:#000">qword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rdx</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">rax</span>                     <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">初始化协程句柄帧前缀的</span><span style="color:#000">_Fn变量</span>
<span style="color:#000">mov</span>         <span style="color:#000">eax</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">10002</span><span style="color:#000">h</span>                              <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">初始化协程句柄帧前缀的</span><span style="color:#000">_Index和_Flags变量</span><span style="color:#a40000">，</span> <span style="color:#a40000">注意这里的</span><span style="color:#000">_Index初始化值为2</span>
<span style="color:#000">mov</span>         <span style="color:#000">r9d</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">2</span>  
<span style="color:#000">cmp</span>         <span style="color:#000">dword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rcx</span><span style="color:#000;font-weight:bold">],</span><span style="color:#0000cf;font-weight:bold">0</span>  
<span style="color:#000">cmovne</span>      <span style="color:#000">eax</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">r9d</span>  
<span style="color:#000">mov</span>         <span style="color:#000">dword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rdx</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">8</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">eax</span>  
<span style="color:#000">xor</span>         <span style="color:#000">eax</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">eax</span>  
<span style="color:#000">mov</span>         <span style="color:#000">dword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">r8</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">eax</span>  
<span style="color:#000">mov</span>         <span style="color:#000">qword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rdx</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">r8</span>                  <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">初始化我们定义的</span> <span style="color:#000">promise_type</span> <span style="color:#a40000">的</span> <span style="color:#000">_ret</span> <span style="color:#a40000">变量</span>
                                                    <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">注意这里的地址是</span> <span style="color:#000">rdx</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000">h</span><span style="color:#a40000">，即协程帧地址</span>
<span style="color:#000">mov</span>         <span style="color:#000">byte</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rdx</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000">r11</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">al</span>  
<span style="color:#000">mov</span>         <span style="color:#000">rcx</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">rdx</span>  
<span style="color:#000">call</span>        <span style="color:#000">test_coroutine</span><span style="color:#a40000">$</span><span style="color:#000">_ResumeCoro</span><span style="color:#a40000">$</span><span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">07FF</span><span style="color:#0000cf;font-weight:bold">6282</span><span style="color:#000">C2FF0h</span><span style="color:#000;font-weight:bold">)</span>  
<span style="color:#000">nop</span>  
<span style="color:#000">add</span>         <span style="color:#000">rsp</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">28</span><span style="color:#000">h</span>  
<span style="color:#000">ret</span>  
</code></pre></div><p><img src="https://i.loli.net/2020/10/14/SXGvEuV4xjCNOPZ.png" alt="coro_init.png"></p>
<p>接着看下_ResumeCoro：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-CPP" data-lang="CPP"><span style="color:#000">mov</span>         <span style="color:#000">qword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rsp</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">8</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">rcx</span>  
<span style="color:#000">sub</span>         <span style="color:#000">rsp</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">38</span><span style="color:#000">h</span>  
<span style="color:#000">mov</span>         <span style="color:#000">r8</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">qword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#000">coro_frame_ptr</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000;font-weight:bold">]</span>     <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">帧地址</span>
<span style="color:#000">movzx</span>       <span style="color:#000">eax</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">word</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">r8</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">8</span><span style="color:#000;font-weight:bold">]</span>                 <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">获得</span><span style="color:#000">_Index值</span>
<span style="color:#000">mov</span>         <span style="color:#000">word</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rsp</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">20</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">],</span><span style="color:#000">ax</span>  
<span style="color:#000">inc</span>         <span style="color:#000">ax</span>                                  <span style="color:#000;font-weight:bold">;</span> <span style="color:#000">_Index</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#a40000">，获得`恢复`索引，</span> <span style="color:#000">_Index为</span><span style="color:#a40000">`暂停`索引</span>
<span style="color:#000">cmp</span>         <span style="color:#000">ax</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">8</span>  
<span style="color:#000">ja</span>          <span style="color:#000">test_coroutine</span><span style="color:#a40000">$</span><span style="color:#000">_ResumeCoro</span><span style="color:#a40000">$</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">12</span><span style="color:#000">Dh</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">07FF</span><span style="color:#0000cf;font-weight:bold">6282</span><span style="color:#000">C311Dh</span><span style="color:#000;font-weight:bold">)</span>  
<span style="color:#000">movsx</span>       <span style="color:#000">rax</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">ax</span>  
<span style="color:#000">lea</span>         <span style="color:#000">rdx</span><span style="color:#000;font-weight:bold">,[</span><span style="color:#000">__ImageBase</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">07FF</span><span style="color:#0000cf;font-weight:bold">6282</span><span style="color:#000">C0000h</span><span style="color:#000;font-weight:bold">)]</span>  <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">获取索引表</span>
<span style="color:#000">mov</span>         <span style="color:#000">ecx</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">dword</span> <span style="color:#000">ptr</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#000">rdx</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000">rax</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">3124</span><span style="color:#000">h</span><span style="color:#000;font-weight:bold">]</span>     <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">计算下条指令的执行地址偏移</span>
<span style="color:#000">add</span>         <span style="color:#000">rcx</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">rdx</span>                             <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">计算下条指令的执行地址</span>
<span style="color:#000">jmp</span>         <span style="color:#000">rcx</span>                                 <span style="color:#000;font-weight:bold">;</span> <span style="color:#a40000">跳转</span>
</code></pre></div><p><img src="https://i.loli.net/2020/10/25/IyThvu8k6rAjbDV.jpg" alt="Inkedcoro_goto_2_LI.jpg"></p>
<p>这段代码可以说是协程实现的最关键的地方了，通过指令<code>mov ecx,dword ptr [rdx+rax*4+3124h] </code>我们甚至可以猜测到隐藏在编译器后面整个实现逻辑。因为编译器在编译时知道我们所有代码的执行地址，所以编译器在生成程序时帮我建立了一张表</p>
<table>
<thead>
<tr>
<th>恢复点地址</th>
</tr>
</thead>
<tbody>
<tr>
<td>恢复点1</td>
</tr>
<tr>
<td>恢复点2</td>
</tr>
<tr>
<td>&hellip;</td>
</tr>
</tbody>
</table>
<p>编译器每次遇到co_await, co_yied, 或者其他需要暂停恢复的地方都往这个表里添加一条记录。在执行时每次暂停，恢复时修改_Index值来动态找到需要跳转的地址。解释下为什么_Index初始化时值为2， 因为第一个暂停点和恢复点时留给cancel操作的。怎么样这里的逻辑是不是有点“达夫设备”的感觉，只是编译器做的更好。下面我们来验证下各个跳转点：</p>
<p><img src="https://i.loli.net/2020/10/25/rX5yeIvYUMgABj3.png" alt="coro_goto_1.png"></p>
<p>从上面的代码可以看出，rcx是一个内存映射好的地址，现在让我们分别加上前面的地址偏移看看jmp最终跳转到的代码时什么。</p>
<ul>
<li>
<p>第1个 rax = rcx + (rcx+0*4+34E8h) = 0x00007ff7eca10000 + 0x000034a1 （注意这里的字节序，需要反过来取值） = 0x00007ff7eca134a1，</p>
</li>
<li>
<p>第2个 rax = rcx + (rcx+1*4+34E8h) = 0x00007ff7eca10000 + 0x000034cb  = 0x00007ff7eca134cb 看下0x00007ff7eca134a1和0x00007ff7eca134cb地址处的代码：
<img src="https://i.loli.net/2020/10/25/swdDnMfXv3FZpSP.png" alt="coro_goto_3.png"></p>
<p>可以很容易的发现这是_resumable_cancel函数的进入和退出点， 同样我们再看看根据后面几个值算出的地址是什么代码。</p>
</li>
<li>
<p>第3个00007FF7ECA13431是 promise_type展开中的 co_await initial_suspend() 代码
<img src="https://i.loli.net/2020/10/25/moVFx5KaIwJ1DqG.png" alt="coro_goto_4.png"></p>
<p>这是 co_await initial_suspend() 的代码</p>
</li>
<li>
<p>第4个0x00007ff7eca12c4f是 第一次进入协程函数的代码。</p>
</li>
<li>
<p>第5个0x00007ff7eca12c4a是 0x00007ff7eca12c4f  失败时的恢复点。</p>
</li>
<li>
<p>第6个0x00007ff7eca12e20是 第一次调用 co_await awaitee() 恢复后代码。</p>
</li>
<li>
<p>第7个0x00007ff7eca12e1b是 0x00007ff7eca12e20  失败时的恢复点。</p>
</li>
<li>
<p>第8个0x00007ff7eca130da是 第二次调用 co_await awaitee() 恢复后代码。</p>
</li>
<li>
<p>第9个0x00007ff7eca130d5是 0x00007ff7eca130da  失败时的恢复点。</p>
<p><img src="https://i.loli.net/2020/10/25/MmLxGjk23shNzKo.png" alt="coro_goto_5.png"></p>
<p>从上图可以看到每次失败时都会跳转到 0x00007ff7eca134a1 即 _resumable_cancel函数的进入点。</p>
</li>
</ul>
<h2 id="总结">总结</h2>
<p>协程最关键的操作是暂停和恢复函数的执行，C++20中的协程借助于编译器在编译期插入相关代码、记录相应跳转点然后运行期根据协程状态计算跳转地址实现协程的各种功能。希望经过本文的讲解和分析后能够帮助大家更为深刻的了解和理解协程的机理，能够让大家放下对协程的恐惧，在以后的使用中能够更加自信。</p>
<p>比如很多人在使用协程时都会想协程时线程安全的吗？其实在我看来这是个伪命题，既然是问是否是线程安全，那么肯定要出现资源被多个线程竞争的情况。但是从我们前面的例子可以看出整个代码都是在一个线程执行的，所以谈不上线程安不安全。当然如果我们改下前面的代码，将resume的调用放在不同线程里面执行，这种情况下协程函数中如果使用了共享的对象，那肯定不是线程安全的。至于网上许多说协程没有线程安全问题应该是指在一个协程函数中多次暂停和恢复，由于是顺序进行的，所以协程对象上的变量在此过程中不会存在竞争，可以说是线程安全的。总之，线程是否安全关键还是看线程而不是协程，协程只是改变代码执行流程的一种手段。</p>
<p>在使用协程时，我们关心的另一问题就是性能效率问题。在理解C++的协程原理后我们知道建立一个协程函数后需要建立个协程对象，这个协程对象中包含承诺对象、形参信息、临时变量、协程句柄。与回调函数比起来真正多出来的应该只有协程句柄内包含的信息，承诺对象相当于返回值， 形参和临时变量回调函数也是少不了的（协程经过编译器优化可以直接在堆上分配），而协程句柄的内存也是极少的（只有一个函数指针和两个状态值）。对于执行时间，协程跳转只需计算一个地址然后直接跳转，效率上不会比一次函数调用差多少。所以C++的协程无论是空间还是时间复杂度性能都是相当可观的。</p>
<p>最后，回到开头我是因为看到asio中的协程示例才开始学习研究C++ Coroutine的，但是此文通篇都没有提及asio， 所以在下篇文章中我会带大家一起学习研究下asio是怎样使用协程实现异步调用的。</p>
<h2 id="参考引用">参考引用</h2>
<p>[1]  <a id="ref_1" href="https://en.cppreference.com/w/cpp/compiler_support" >C++ compiler support</a><code>[EB/OL].</code>  <a href="https://en.cppreference.com/w/cpp/compiler_support">https://en.cppreference.com/w/cpp/compiler_support</a></p>
<p>[2]  <a id="ref_2" href="https://en.wikipedia.org/wiki/%22Hello,_World!%22_program" >&ldquo;Hello, World!&rdquo; program</a><code>[EB/OL].</code><a href="https://en.wikipedia.org/wiki/%22Hello,_World!%22_program">https://en.wikipedia.org/wiki/%22Hello,_World!%22_program</a></p>
<p>[3]  <a id="ref_3" href="https://gcc.gnu.org/projects/cxx-status.html#cxx98" >C++ Standards Support in GCC</a><code>[EB/OL].</code><a href="https://gcc.gnu.org/projects/cxx-status.html#cxx98">https://gcc.gnu.org/projects/cxx-status.html#cxx98</a></p>
<p>[4]  <a id="ref_4" href="https://en.cppreference.com/w/cpp/thread/get_id" >std::this_thread::get_id</a><code>[EB/OL].</code><a href="https://en.cppreference.com/w/cpp/thread/get_id">https://en.cppreference.com/w/cpp/thread/get_id</a></p>
<p>[5]  <a id="ref_5" href="https://en.cppreference.com/w/cpp/language/user_literal%23Standard_library" >User-defined literals</a><code>[EB/OL].</code><a href="https://en.cppreference.com/w/cpp/language/user_literal%23Standard_library">https://en.cppreference.com/w/cpp/language/user_literal%23Standard_library</a></p>
<p>[6]  <a id="ref_6" href="https://aceld.gitbooks.io/libevent/content/31_reactorfan_ying_dui_mo_shi.html" >reactor反应堆模式</a><code>[EB/OL].</code><a href="https://aceld.gitbooks.io/libevent/content/31_reactorfan_ying_dui_mo_shi.html">https://aceld.gitbooks.io/libevent/content/31_reactorfan_ying_dui_mo_shi.html</a></p>
<p>[7]  <a id="ref_7" href="https://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/async.html" >Proactor and Boost.Asio</a><code>[EB/OL].</code><a href="https://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/async.html">https://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/async.html</a></p>
<p>[8]  <a id="ref_8" href="https://en.cppreference.com/w/cpp/language/coroutines" >Coroutines (C++20)
</a><code>[EB/OL].</code><a href="https://en.cppreference.com/w/cpp/language/coroutines">https://en.cppreference.com/w/cpp/language/coroutines</a></p>
<p>[9]  <a id="ref_9" href="https://zh.wikipedia.org/zh-cn/%E5%8D%8F%E7%A8%8B" >协程
</a><code>[EB/OL].</code><a href="https://zh.wikipedia.org/zh-cn/%E5%8D%8F%E7%A8%8B">https://zh.wikipedia.org/zh-cn/%E5%8D%8F%E7%A8%8B</a></p>
<p>[10]  <a id="ref_10" href="https://github.com/Tencent/libco" >Tencent/libco</a><code>[EB/OL].</code><a href="https://github.com/Tencent/libco">https://github.com/Tencent/libco</a></p>
<p>[11]  <a id="ref_11" href="http://www.golangpatterns.info/concurrency/coroutines" >Go Language Patterns/Coroutines</a><code>[EB/OL].</code><a href="http://www.golangpatterns.info/concurrency/coroutines">http://www.golangpatterns.info/concurrency/coroutines</a></p>
<p>[12]  <a id="ref_12" href="https://linux.die.net/man/2/setcontext" >setcontext(2) - Linux man page</a><code>[EB/OL].</code><a href="https://linux.die.net/man/2/setcontext">https://linux.die.net/man/2/setcontext</a></p>
<p>[13]  <a id="ref_13" href="https://www.boost.org/doc/libs/1_74_0/libs/context/doc/html/context/overview.html" >Boost.Context</a><code>[EB/OL].</code><a href="https://www.boost.org/doc/libs/1_74_0/libs/context/doc/html/context/overview.html">https://www.boost.org/doc/libs/1_74_0/libs/context/doc/html/context/overview.html</a></p>
<p>[14]  <a id="ref_14" href="https://github.com/Tencent/libco" >Libco</a><code>[EB/OL].</code><a href="https://github.com/Tencent/libco">https://github.com/Tencent/libco</a></p>
<p>[15]  <a id="ref_15" href="https://mthli.xyz/coroutines-in-c/" >使用 C 语言实现协程</a><code>[EB/OL].</code><a href="https://mthli.xyz/coroutines-in-c/">https://mthli.xyz/coroutines-in-c/</a></p>
<p>[16]  <a id="ref_16" href="https://en.wikipedia.org/wiki/Duff%27s_device" >达夫设备</a><code>[EB/OL].</code><a href="https://en.wikipedia.org/wiki/Duff%27s_device">https://en.wikipedia.org/wiki/Duff%27s_device</a></p>
<p>[17]  <a id="ref_17" href="https://www.bennyhuo.com/2019/12/01/coroutine-implementations/" >破解 Kotlin 协程 番外篇(2) - 协程的几类常见的实现</a><code>[EB/OL].</code><a href="https://www.bennyhuo.com/2019/12/01/coroutine-implementations/">https://www.bennyhuo.com/2019/12/01/coroutine-implementations/</a></p>
<p>[18]  <a id="ref_18" href="https://searchcio.techtarget.com.cn/whatis/8-28809/" >infinite sequence：无限序列</a><code>[EB/OL].</code><a href="https://searchcio.techtarget.com.cn/whatis/8-28809/">https://searchcio.techtarget.com.cn/whatis/8-28809/</a></p>
<p>[19]  <a id="ref_19" href="https://zh.wikipedia.org/wiki/%E6%83%B0%E6%80%A7%E6%B1%82%E5%80%BC" >惰性求值</a><code>[EB/OL].</code><a href="https://zh.wikipedia.org/wiki/%E6%83%B0%E6%80%A7%E6%B1%82%E5%80%BC">https://zh.wikipedia.org/wiki/%E6%83%B0%E6%80%A7%E6%B1%82%E5%80%BC</a></p>
<p>[20]  <a id="ref_20" href="https://github.com/lewissbaker/cppcoro" >CppCoro - A coroutine library for C++</a><code>[EB/OL].</code><a href="https://github.com/lewissbaker/cppcoro">https://github.com/lewissbaker/cppcoro</a></p>
<p>[21]  <a id="ref_21" href="https://raw.githubusercontent.com/CppCon/CppCon2016/master/Presentations/C%2B%2B%20Coroutines%20-%20Under%20The%20Covers/C%2B%2B%20Coroutines%20-%20Under%20The%20Covers%20-%20Gor%20Nishanov%20-%20CppCon%202016.pdf" >Exploring the C++ Coroutine</a><code>[EB/OL].</code><a href="https://raw.githubusercontent.com/CppCon/CppCon2016/master/Presentations/C%2B%2B%20Coroutines%20-%20Under%20The%20Covers/C%2B%2B%20Coroutines%20-%20Under%20The%20Covers%20-%20Gor%20Nishanov%20-%20CppCon%202016.pdf">https://raw.githubusercontent.com/CppCon/CppCon2016/master/Presentations/C%2B%2B%20Coroutines%20-%20Under%20The%20Covers/C%2B%2B%20Coroutines%20-%20Under%20The%20Covers%20-%20Gor%20Nishanov%20-%20CppCon%202016.pdf</a></p>
<p>[22]  <a id="ref_22" href="https://luncliff.github.io/coroutine/ppt/%5BEng%5DExploringTheCppCoroutine.pdf" >Exploring the C++ Coroutine</a><code>[EB/OL].</code><a href="https://luncliff.github.io/coroutine/ppt/%5BEng%5DExploringTheCppCoroutine.pdf">https://luncliff.github.io/coroutine/ppt/%5BEng%5DExploringTheCppCoroutine.pdf</a></p>
<!-- 

## 未整理部分

天涯明月刀-无栈协程的应用 https://gameinstitute.qq.com/community/detail/107515

有栈协程与无栈协程 https://mthli.xyz/stackful-stackless/

luncliff/coroutineExploring MSVC Coroutine 
https://luncliff.github.io/coroutine/articles/exploring-msvc-coroutine/
https://luncliff.github.io/posts/Exploring-MSVC-Coroutine.html
https://luncliff.github.io/coroutine/ppt/%5BEng%5DExploringTheCppCoroutine.pdf

coroutines https://github.com/topics/coroutines?l=c%2B%2B

汇编 https://cs.lmu.edu/~ray/notes/nasmtutorial/
汇编编译 https://godbolt.org/


N3858.pdf https://isocpp.org/files/papers/N3858.pdf

达夫设备 https://mthli.xyz/coroutines-in-c/

其实上面说的是有一些问题，因为这个执行权的切换实际上是（调用者–被调用者）之间的切换，对称就是它们之间都是平等的，就是假如A协程执行了B，C协程，那么B协程可以切换回A，也可以切换回C。而非对称只能是B切换回A，A切换回C，C再切换回A，以此类推。
https://blog.csdn.net/weixin_43705457/article/details/106924435

callback hell https://medium.com/@quyetvv/async-flow-from-callback-hell-to-promise-to-async-await-2da3ecfff997

 [The reasons why 64-bit programs require more stack memory](https://software.intel.com/content/www/us/en/develop/blogs/the-reasons-why-64-bit-programs-require-more-stack-memory.html)   https://software.intel.com/content/www/us/en/develop/blogs/the-reasons-why-64-bit-programs-require-more-stack-memory.html

 C++ Coroutines: Understanding Symmetric Transfer https://lewissbaker.github.io/2020/05/11/understanding_symmetric_transfer 

C++网络编程之asio(五)——在asio中使用协程 https://zhuanlan.zhihu.com/p/58784652

# 
--><blockquote>
</blockquote>

	

	<ul class="list-unstyled d-flex justify-content-between align-items-center mb-0 pt-5">
  <li>
    <a  class="btn btn-primary  disabled"><span class="mr-1">←</span> 上一页</a>
  </li>
    <a href="/blog/2020/11/18/c-%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%B7%A5%E5%85%B7/" class="btn btn-primary ">下一页 <span class="ml-1">→</span></a>
  </li>
</ul>

</div><div id="utter-container"></div>
    <script src="https://utteranc.es/client.js"
        repo= 'gqw/hugocomment'
        issue-term= "title"
        theme= 'github-light'
        crossorigin= "anonymous"
        async>
    </script>


          </main>
        </div>
      </div>
      
<footer class="bg-dark py-5 row d-print-none">
  <div class="container-fluid mx-sm-5">
    <div class="row">
      <div class="col-6 col-sm-4 text-xs-center order-sm-2">
        
        
        
<ul class="list-inline mb-0">
  
  <li class="list-inline-item mx-2 h3" data-toggle="tooltip" data-placement="top" title="User mailing list" aria-label="User mailing list">
    <a class="text-white" target="_blank" href="gqwmail@qq.com">
      <i class="fa fa-envelope"></i>
    </a>
  </li>
  
</ul>

        
        
      </div>
      <div class="col-6 col-sm-4 text-right text-xs-center order-sm-3">
        
        
        
<ul class="list-inline mb-0">
  
  <li class="list-inline-item mx-2 h3" data-toggle="tooltip" data-placement="top" title="GitHub" aria-label="GitHub">
    <a class="text-white" target="_blank" href="https://github.com/gqw">
      <i class="fab fa-github"></i>
    </a>
  </li>
  
</ul>

        
        
      </div>
      <div class="col-12 col-sm-4 text-center py-2 order-sm-2">
        <small class="text-white">&copy; 2020 The Docsy Authors All Rights Reserved</small>
        
		
			<p class="mt-2"><a href="/about/">关于</a></p>
		
      </div>
    </div>
  </div>
</footer>


<script type="text/javascript"
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    tex2jax: {
      inlineMath: [['$','$']],
      displayMath: [['$$','$$']],
      processEscapes: true,
      processEnvironments: true,
      skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
      TeX: { equationNumbers: { autoNumber: "AMS" },
          extensions: ["AMSmath.js", "AMSsymbols.js"] }
      }
  });
  </script>

    </div>
    
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>







<script src="/js/main.min.b99ff36446a5ac4634439b55123fb4bc85e1a0423d2b19b088890d1e33f503a6.js" integrity="sha256-uZ/zZEalrEY0Q5tVEj&#43;0vIXhoEI9KxmwiIkNHjP1A6Y=" crossorigin="anonymous"></script>



  </body>
</html>